/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#define CFRONT_PLATFORM_C
#include "platform.h"
#include "cfront.h"
#include "eval.h"
#include "init.h"
#include "fe_flags.h"
#include "errors.h"
#include "cpp.h"

#define Str2(x) #x
#define Str(x) Str2(x)

/*
   Little endian means the least significant byte is stored first.
   A byte has bits_per_char bits.
*/

Bool test_little_endian ()
{
  long word = 0x12345678;
  long mask = ~(-1 << bits_per_char);
  return * (unsigned char *) &word == (word & mask);
}

static char *user_fname = NULL;

AP_Integer get_cpp_val (char *symbol, Bool req, char *dflt)
{
  AP_Integer x;
  expression expr;
  char *def = CPP_alias (symbol);
  if (!def)
  {
    /* Warn only if the symbol is required. */
    if (req) missing_definition (CPP_dummy_token(user_fname), symbol, dflt);
    def = dflt;
  }
  expr = Parse_expression (def, 0);
  ast_decorate (expression, expr, NULL, NULL, cfront_scope (expr));
  x = EvalIntExpr (expr);
  if (!x)
  {
    mpz_t z;
    integral_expression (CPP_dummy_token(user_fname), symbol);
    mpz_init_set_si (z, 0);
    x = AP_Int (z);
  }
#if 0
  fprintf (stderr, "Symbol %s = '%s'%s, evaluates to ", symbol, def, def==dflt ? " (default)" : "");
  gmp_fprintf (stderr, "%Zd.\n", *x);
#endif
  return x;
}

#ifndef LLONG_MIN
#define LLONG_MIN LONG_MIN
#endif

#ifndef LLONG_MAX
#define LLONG_MAX LONG_MAX
#endif

#ifndef ULLONG_MAX
#define ULLONG_MAX ULONG_MAX
#endif

void
host_integral_ranges (char *fname)
{
  Bool adj = set_type_adjust (FALSE);
  user_fname = fname;

  /* for long long assume ANSI-C '99 spec */

  mpz_set (signed_char_min,       *get_cpp_val ("SCHAR_MIN", TRUE, Str (SCHAR_MIN)));
  mpz_set (signed_char_max,       *get_cpp_val ("SCHAR_MAX", TRUE, Str (SCHAR_MAX)));
  mpz_set (unsigned_char_max,     *get_cpp_val ("UCHAR_MAX", TRUE, Str (UCHAR_MAX)));
  mpz_set (signed_short_min,      *get_cpp_val ("SHRT_MIN",  TRUE, Str (SHRT_MIN)));
  mpz_set (signed_short_max,      *get_cpp_val ("SHRT_MAX",  TRUE, Str (SHRT_MAX)));
  mpz_set (unsigned_short_max,    *get_cpp_val ("USHRT_MAX", TRUE, Str (USHRT_MAX)));
  mpz_set (signed_int_min,        *get_cpp_val ("INT_MIN",   TRUE, Str (INT_MIN)));
  mpz_set (signed_int_max,        *get_cpp_val ("INT_MAX",   TRUE, Str (INT_MAX)));
  mpz_set (unsigned_int_max,      *get_cpp_val ("UINT_MAX",  TRUE, Str (UINT_MAX)));
  mpz_set (signed_long_int_min,   *get_cpp_val ("LONG_MIN",  TRUE, Str (LONG_MIN)));
  mpz_set (signed_long_int_max,   *get_cpp_val ("LONG_MAX",  TRUE, Str (LONG_MAX)));
  mpz_set (unsigned_long_int_max, *get_cpp_val ("ULONG_MAX", TRUE, Str (ULONG_MAX)));
  mpz_set (void_pointer_max,      *get_cpp_val ("ULONG_MAX", TRUE, Str (ULONG_MAX)));

  mpz_init_set_ui (unsigned_char_min,      0);
  mpz_init_set_ui (unsigned_short_min,     0);
  mpz_init_set_ui (unsigned_int_min,       0);
  mpz_init_set_ui (unsigned_long_int_min,  0);
  mpz_init_set_ui (unsigned_long_long_min, 0);
  mpz_init_set_ui (void_pointer_min, 0);

  if (have_longlong) {
    mpz_set (signed_long_long_min,   *get_cpp_val ("LLONG_MIN",  TRUE, Str (LLONG_MIN)));
    mpz_set (signed_long_long_max,   *get_cpp_val ("LLONG_MAX",  TRUE, Str (LLONG_MAX)));
    mpz_set (unsigned_long_long_max, *get_cpp_val ("ULLONG_MAX", TRUE, Str (ULLONG_MAX)));
  } else {
    /* Default to signed, unsigned long types. */
    mpz_set (signed_long_long_min,   *get_cpp_val ("LLONG_MIN",  FALSE, Str (LONG_MIN)));
    mpz_set (signed_long_long_max,   *get_cpp_val ("LLONG_MAX",  FALSE, Str (LONG_MAX)));
    mpz_set (unsigned_long_long_max, *get_cpp_val ("ULLONG_MAX", FALSE, Str (ULONG_MAX)));
  }

#warning TODO: determine sizeofs and alignments (if possible) of the standard types on the host platform.

  set_type_adjust (adj);
}

void
default_integral_ranges ()
{
  Bool adj = set_type_adjust (FALSE);

  mpz_init_set_si (signed_char_min,         SCHAR_MIN);
  mpz_init_set_si (signed_char_max,         SCHAR_MAX);
  mpz_init_set_si (signed_short_min,        SHRT_MIN);
  mpz_init_set_si (signed_short_max,        SHRT_MAX);
  mpz_init_set_si (signed_int_min,          INT_MIN);
  mpz_init_set_si (signed_int_max,          INT_MAX);
  mpz_init_set_si (signed_long_int_min,     LONG_MIN);
  mpz_init_set_si (signed_long_int_max,     LONG_MAX);
  mpz_init_set_str (signed_long_long_min,   Str(LLONG_MIN), 10);
  mpz_init_set_str (signed_long_long_max,   Str(LLONG_MAX), 10);

  mpz_init_set_ui (unsigned_char_min,       0);
  mpz_init_set_ui (unsigned_char_max,       UCHAR_MAX);
  mpz_init_set_ui (unsigned_short_min,      0);
  mpz_init_set_ui (unsigned_short_max,      USHRT_MAX);
  mpz_init_set_ui (unsigned_int_min,        0);
  mpz_init_set_ui (unsigned_long_int_min,   0);
  mpz_init_set_ui (unsigned_int_max,        UINT_MAX);
  mpz_init_set_ui (unsigned_long_long_min,  0);
  mpz_init_set_ui (unsigned_long_int_max,   ULONG_MAX);
  mpz_init_set_str (unsigned_long_long_max, Str(ULLONG_MAX), 10);

  mpz_init_set_ui (void_pointer_min,        0);
  mpz_init_set_ui (void_pointer_max,        ULONG_MAX);

  set_type_adjust (adj);
}
